#pragma once
#include<iostream>
#include<vector>
#include"util.hpp"
#include"session.hpp"
#include"room.hpp"
#include"online.hpp"
#include"matcher.hpp"
#include"db.hpp"
#include"logger.hpp"
#include <functional>


#define WEBROOT "./webroot/"
class gobang_server
{
    private:
    //创建回复报文
    void creat_resp(wsserver_t::connection_ptr conn,bool res,std::string rea,websocketpp::http::status_code::value code)
    {
        Json::Value resp;
        resp["result"]=res;
        resp["reason"]=rea;
        std::string body;
        json_util::Serialize(resp,body);
        conn->set_body(body);
        conn->set_status(code);
        conn->append_header("Content-Type","application/json");
        conn->append_header("Content-Length",std::to_string(body.size()));
    }
    //处理http静态资源请求
    void File_handle(websocketpp::connection_hdl hdl)
    {
        //获取连接
        wsserver_t::connection_ptr conn=_wsser.get_con_from_hdl(hdl);
        //获取请求信息和url
        websocketpp::http::parser::request req=conn->get_request();
        std::string url=req.get_uri();
        std::string realpath=_webroot+url;
        if(realpath.back()=='/')
        {
            realpath=_webroot+"login.html";
        }
        //读取指定的文件内容：
        std::string body;
        bool ret=file_util::file_read(realpath,body);
        if(ret==false)
        {
            DBG_LOG("用户请求了一个非法文件！！");
            file_util::file_read(_webroot+"notfound.html",body);
            conn->set_status(websocketpp::http::status_code::not_found);
        }
        else
        {
            conn->set_status(websocketpp::http::status_code::ok);
        }
        conn->set_body(body);
    }
    //处理http登录请求
    void Login(websocketpp::connection_hdl hdl)
    {
        wsserver_t::connection_ptr conn=_wsser.get_con_from_hdl(hdl);
        std::string req_body=conn->get_request_body();
        Json::Value req;
        bool ret1=json_util::Unserialize(req_body,req);
        std::cout<<req_body<<std::endl;
        std::cout<<req<<std::endl;
        if(ret1==false)
        {
            //请求正文格式错误
            creat_resp(conn,false,"登录请求格式错误",websocketpp::http::status_code::bad_request);
            return;
        }
        if(req["username"].isNull()||req["password"].isNull())
        {
            //用户名和密码有一个为空
            creat_resp(conn,false,"用户名或密码不能为空",websocketpp::http::status_code::bad_request);
            return;
        }
        DBG_LOG("开始在数据库中查询用户信息");
        //在数据库中查询用户信息
        bool ret=_ut.login(req);
        if(ret==false)
        {
            creat_resp(conn,false,"用户名密码错误",websocketpp::http::status_code::bad_request);
            return;
        }
        DBG_LOG("开始创建session");
        //创建session
        std::cout<<req<<std::endl;
        session_ptr sp=_sm.Create_session(req["id"].asUInt64());
        if(sp.get()==nullptr)
        {
            //创建session失败
            creat_resp(conn,false,"创建session失败",websocketpp::http::status_code::internal_server_error);
            return;
        }
        //给session设置过期时间
        _sm.Set_expeir_time(sp->Get_ssid(),SESSION_TIMEOUT);
        //给前端返回对应session的ssid
        std::string cookie_ssid="SSID="+std::to_string(sp->Get_ssid());
        conn->append_header("Set-Cookie",cookie_ssid);
        creat_resp(conn,false,"登录成功",websocketpp::http::status_code::ok);
        return;
    }
    //处理http注册请求
    void Reg(websocketpp::connection_hdl hdl)
    {
        //获取连接
        wsserver_t::connection_ptr conn=_wsser.get_con_from_hdl(hdl);
        std::string req_body=conn->get_request_body();
        Json::Value req;
        bool ret1=json_util::Unserialize(req_body,req);
        if(ret1==false)
        {
            //请求正文格式错误
            creat_resp(conn,false,"请求格式错误",websocketpp::http::status_code::bad_request);
            return;
        }
        if(req["username"].isNull()||req["password"].isNull())
        {
            //用户名和密码有一个为空
            creat_resp(conn,false,"用户名或密码不能为空",websocketpp::http::status_code::bad_request);
            return;
        }
        //在数据库中新增用户信息
        bool ret=_ut.insert(req);
        if(ret==false)
        {
            //新增用户信息失败，用户已经存在
            creat_resp(conn,false,"用户名已存在",websocketpp::http::status_code::bad_request);
            return;
        }
        creat_resp(conn,true,"注册成功",websocketpp::http::status_code::ok);
        return;
    }
    bool get_cookie_value(std::string& cookieinfo,std::string key,std::string& value)
    {
        std::vector<std::string> cookie_arr;
        string_util::split(cookieinfo,"; ",cookie_arr);
        for(auto arr:cookie_arr)
        {
            std::vector<std::string> temp_arr;
            string_util::split(arr,"=",temp_arr);
            if(temp_arr.size()!=2)
            {
                continue;
            }
            else
            {
                if(temp_arr[0]==key)
                    value=temp_arr[1];
                return true;
            }
        }
        return false;
    }
    //处理http获取用户信息的请求
    void Get_info(websocketpp::connection_hdl hdl)
    {
        wsserver_t::connection_ptr conn=_wsser.get_con_from_hdl(hdl);
        std::string cookie_info=conn->get_request_header("Cookie");

        std::cout<<cookie_info<<std::endl;
        if(cookie_info.empty())
        {
            //没有对应的cookie信息，请求格式错误，重新登录
            return creat_resp(conn,false,"没有对应的cookie信息，请重新登录",websocketpp::http::status_code::bad_request);
        }
        //对Cookie_info进行字符串分割处理
        std::string ssid;
        bool ret=get_cookie_value(cookie_info,"SSID",ssid);
        if(ret==false)
        {
            //没有对应的cookie信息，请求格式错误，重新登录
            return creat_resp(conn,false,"字符串分割处理失败，请重新登录",websocketpp::http::status_code::bad_request);
        }
        //查询对应的session信息
        session_ptr ss=_sm.Get_by_ssid(std::stol(ssid));
        if(ss.get()==nullptr)
        {
            //说明session信息已经过期，重新登录
            return creat_resp(conn,false,"用户信息已过期，请重新登录",websocketpp::http::status_code::bad_request);
        }
        //获取session中的用户id并根据id获取该用户的详细信息
        Json::Value user;
        std::string body;
        bool res=_ut.select_by_id(ss->GetUser(),user);
        json_util::Serialize(user,body);
        conn->set_body(body);
        conn->set_status(websocketpp::http::status_code::ok);
        conn->append_header("Content-Type","application/json");
        conn->append_header("Content-Length",std::to_string(body.size()));
        //刷新该用户的session过期时间**************************************************************************
        _sm.Set_expeir_time(std::stol(ssid),SESSION_TIMEOUT);
    }
    void http_callback(websocketpp::connection_hdl hdl)
    {
        //处理http请求
        DBG_LOG("接收到一个HTTP请求开始处理");
        wsserver_t::connection_ptr conn=_wsser.get_con_from_hdl(hdl);
        //./webroot/register.html
        websocketpp::http::parser::request req=conn->get_request();
        std::string method=req.get_method();
        std::string url=req.get_uri();
        if(method=="POST"&&url=="/reg")
        {
            return Reg(hdl);
        }
        else if(method=="POST"&&url=="/login")
        {
            DBG_LOG("进入登录接口");
            return Login(hdl);
        }
        else if(method=="GET"&&url=="/info")
        {
            return Get_info(hdl);
        }
        else
        {
            return File_handle(hdl);
        }
        DBG_LOG("接收到一个HTTP请求处理结束");
    }
    void messager_callback(websocketpp::connection_hdl hdl,wsserver_t::message_ptr ptr)
    {
    }
    //创建一个websocket回复报文
    void creat_ws_resp(wsserver_t::connection_ptr conn,std::string optype,bool result,std::string reason)
    {
        Json::Value wsresp;
        wsresp["optype"]=optype;
        wsresp["result"]=result;
        wsresp["reason"]=reason;
        std::string body;
        json_util::Serialize(wsresp,body);
        conn->send(body);
    }
    //处理游戏大厅的ws长连接
    void handle_open_gamehall(websocketpp::connection_hdl hdl)
    {
        wsserver_t::connection_ptr conn=_wsser.get_con_from_hdl(hdl);
        std::string cookie_info=conn->get_request_header("Cookie");
        if(cookie_info.empty())
        {
            //没有对应的cookie信息，请求格式错误，重新登录
            return creat_ws_resp(conn,"hall_ready",false,"没有对应的Cookie信息，请重新登录！！");
        }
        //对Cookie_info进行字符串分割处理
        std::string ssid;
        bool ret=get_cookie_value(cookie_info,"SSID",ssid);
        if(ret==false)
        {
            //没有对应的cookie信息，请求格式错误，重新登录
            return creat_ws_resp(conn,"hall_ready",false,"字符串分割处理失败，请重新登录!!");
        }
        //查询对应的session信息
        session_ptr ss=_sm.Get_by_ssid(std::stol(ssid));
        if(ss.get()==nullptr)
        {
            //说明session信息已经过期，重新登录
            return creat_ws_resp(conn,"hall_ready",false,"用户信息已过期，请重新登录");
        }
        //说明用户已经成功登录，判断用户是否重复登录
        uint64_t uid=ss->GetUser();
        bool isin=_om.is_in_game_hall(uid);
        if(isin==true)
        {
            return creat_ws_resp(conn,"hall_ready",false,"同一用户不能重复登录！！");
        }
        //将用户加入在线用户管理的游戏大厅
        _om.enter_game_hall(uid,conn);
        //将session设置为永久
        _sm.Set_expeir_time(std::stol(ssid),SESSION_FOREVER);
        return creat_ws_resp(conn,"hall_ready",true,"用户游戏大厅长连接建立服务完成！");
    }
    //处理游戏房间的ws长连接
    void handle_open_gameroom(websocketpp::connection_hdl hdl)
    {

    }
    void open_callback(websocketpp::connection_hdl hdl)
    {
       //处理websocket请求
        DBG_LOG("接收到一个websocket请求开始处理");
        wsserver_t::connection_ptr conn=_wsser.get_con_from_hdl(hdl);
        //./webroot/register.html
        websocketpp::http::parser::request req=conn->get_request();
        std::string method=req.get_method();
        std::string url=req.get_uri();
        if(url=="/hall")
        {

            return handle_open_gamehall(hdl);
        }
        else if(url=="/room")
        {
            return handle_open_gameroom(hdl);
        }
    }
    //用户从游戏大厅退出的回调函数：
    void handle_close_gamehall(websocketpp::connection_hdl hdl)
    {
        wsserver_t::connection_ptr conn=_wsser.get_con_from_hdl(hdl);
        std::string cookie_info=conn->get_request_header("Cookie");
        if(cookie_info.empty())
        {
            //没有对应的cookie信息，请求格式错误，重新登录
            return creat_ws_resp(conn,"hall_ready",false,"没有对应的Cookie信息，请重新登录！！");
        }
        //对Cookie_info进行字符串分割处理
        std::string ssid;
        bool ret=get_cookie_value(cookie_info,"SSID",ssid);
        if(ret==false)
        {
            //没有对应的cookie信息，请求格式错误，重新登录
            return creat_ws_resp(conn,"hall_ready",false,"字符串分割处理失败，请重新登录!!");
        }
        //查询对应的session信息
        session_ptr ss=_sm.Get_by_ssid(std::stol(ssid));
        //1.将用户从用户管理模中移除
        _om.exit_game_hall(ss->GetUser());
        //2.将session设置为指定时间后过期
        _sm.Set_expeir_time(ss->Get_ssid(),SESSION_TIMEOUT);
    }
    void handle_close_gameroom(websocketpp::connection_hdl hdl)
    {}
    void close_callback(websocketpp::connection_hdl hdl)
    {
        //处理websocket请求
        DBG_LOG("接收到一个websocket请求开始处理");
        wsserver_t::connection_ptr conn=_wsser.get_con_from_hdl(hdl);
        //./webroot/register.html
        websocketpp::http::parser::request req=conn->get_request();
        std::string method=req.get_method();
        std::string url=req.get_uri();
        if(url=="/hall")
        {

            return handle_close_gamehall(hdl);
        }
        else if(url=="/room")
        {
            return handle_close_gameroom(hdl);
        }
    }
    public:
    //构造函数
    gobang_server(
        std::string &host,
        std::string &user,
        std::string &pass,
        std::string &db,
        uint16_t port = 3306
    )
    :_ut(host,user,pass,db,port)
    ,_mm(&_om,&_rm,&_ut)
    ,_om()
    ,_rm(&_ut,&_om)
    ,_sm(&_wsser)
    ,_webroot(WEBROOT)
    {
        _wsser.set_access_channels(websocketpp::log::alevel::none);
        //初始化服务器
        _wsser.init_asio();
        //设置回调函数
        _wsser.set_open_handler(bind(&gobang_server::open_callback,this,std::placeholders::_1));
        _wsser.set_close_handler(bind(&gobang_server::close_callback,this,std::placeholders::_1));
        _wsser.set_http_handler(bind(&gobang_server::http_callback,this,std::placeholders::_1));
        _wsser.set_message_handler(bind(&gobang_server::messager_callback,this,std::placeholders::_1, std::placeholders::_2));
    }
    void start(int port) 
    {
        _wsser.listen(port);
        _wsser.start_accept();
        DBG_LOG("服务器初始化成功！！");
        _wsser.run();
        
    }
    ~gobang_server()
    {}
    private:
    user_table _ut;
    match_manager _mm;
    online_manager _om;
    room_mamager _rm;
    session_manager _sm;
    wsserver_t _wsser;
    std::string _webroot;
};